package filter

import (
	"cgs-server/helper"
	"reflect"
	"strconv"
	"strings"
	"time"

	"git.mapchang.com/go/errors"
	"go.mongodb.org/mongo-driver/bson"
)

type FilterType int
type FilterValue struct {
	QueryFlag string      // 查询符号
	Value     interface{} // 查询的数据库语法值
	BaseDataType
	FilterType
	TargetDataType BaseDataType
}

func NewFilterValue(queryFlag string, val interface{}, filterDataType BaseDataType, targetDataType BaseDataType) FilterValue {
	return FilterValue{
		QueryFlag:      queryFlag,
		Value:          val,
		BaseDataType:   filterDataType,
		TargetDataType: targetDataType,
	}
}

// GetFilterExpression 获取查询参数
// value 需要满足的值
func (filterValue FilterValue) GetFilterExpression(value string, dataType DataType) interface{} {
	v := filterValue.Value
	switch filterValue.BaseDataType {
	case String:
		return caseString(filterValue.QueryFlag, v.(string), value)
	case Boolean:
		return caseBool(filterValue.QueryFlag, v.(bool))
	case Number:
		return caseInt(filterValue.QueryFlag, value)
	case Any:
		return caseAny(filterValue.QueryFlag, value, v, dataType)
	}

	return nil
}

func caseAny(flag, value string, oldValue interface{}, dataType DataType) interface{} {
	// todo 有些数据类型的值有可能是 字符串或数值，这里需要判断一下，比如下拉框，需要加一个 case Any
	switch GetDataTypeProperty(dataType).BaseDataType {
	case String:
		return caseString(flag, oldValue.(string), value)
	case Number:
		return caseInt(flag, value)
	case Any:
		return caseAnyDataType(flag, value, oldValue, dataType)
	}

	return bson.M{}
}

func caseString(flag, oldValue, value string) interface{} {
	// 如果 queryFlag 为空，直接返回值
	if flag == "" {
		return strings.ReplaceAll(oldValue, "{Value}", value)
	}

	result := bson.M{
		flag: strings.ReplaceAll(oldValue, "{Value}", value),
	}

	return result
}

func caseInt(flag string, value string) interface{} {
	number, err := strconv.ParseFloat(value, 64)
	if err != nil {
		return bson.M{}
	}
	if flag == "" {
		return number
	}

	return bson.M{flag: number}
}

func caseBool(flag string, value bool) interface{} {
	return bson.M{
		flag: value,
	}
}

func caseAnyDataType(flag, value string, oldValue interface{}, dataType DataType) interface{} {
	switch dataType {
	case Radio:
		// value 是string的时候
		strFilter := caseString(flag, oldValue.(string), value)
		// value 是int的时候
		_, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return strFilter
		} else {
			intFilter := caseInt(flag, value)
			arr := bson.A{strFilter, intFilter}
			return arr
		}

	case Select:
		strFilter := caseString(flag, oldValue.(string), value)
		// value 是int的时候
		_, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return strFilter
		} else {
			intFilter := caseInt(flag, value)
			arr := bson.A{strFilter, intFilter}
			return arr
		}
	}

	return bson.M{}
}

// FilterData 工作流过滤条件
// compareVal 待比较的值
// fv 满足条件的值
// dataType 数据类型
func (filterValue *FilterValue) FilterData(compareVal interface{}, fv string, dataType DataType) bool {
	if compareVal == nil {
		return filterValue.FilterType == IsNull
	}

	switch filterValue.TargetDataType {
	case String:
		return filterValue.caseString(compareVal, fv)
	case Number:
		return filterValue.caseNumber(compareVal, fv, "")
	case Any:
		return filterValue.caseAny(compareVal, fv, dataType)
	}

	return false
}

func (filterValue *FilterValue) caseString(compareVal interface{}, fv string) bool {
	if compareVal == nil {
		return filterValue.FilterType == IsNull
	}
	v, ok := compareVal.(string)
	if !ok {
		return false
	}
	switch filterValue.FilterType {
	case DefaultFilterType:
		return strings.Compare(v, fv) == 0
	case Like:
		return strings.Contains(v, fv)
	case Eq:
		return strings.Compare(v, fv) == 0
	case Start:
		return strings.HasPrefix(v, fv)
	case End:
		return strings.HasSuffix(v, fv)
	case NotContain:
		return !strings.Contains(v, fv)
	case Ne:
		return strings.Compare(v, fv) != 0
	case IsNull:
		return v == ""
	case HasValue:
		return v != ""
	}

	return false
}

func (filterValue *FilterValue) caseNumber(compareVal interface{}, fv string, fv2 string) bool {
	if compareVal == nil {
		return filterValue.FilterType == IsNull
	}
	cv, newFv, err := parseStringFloat(compareVal, fv)
	if err != nil {
		return false
	}

	switch filterValue.FilterType {
	case DefaultFilterType:
		return cv == newFv
	case Eq:
		return cv == newFv
	case Ne:
		return cv != newFv
	case IsNull:
		return false
	case HasValue:
		return true
	case Between:
		_, newFv2, err := parseStringFloat(compareVal, fv2)
		if err != nil {
			return false
		}
		return cv >= newFv && cv <= newFv2
	case NotBetween:
		_, newFv2, err := parseStringFloat(compareVal, fv2)
		if err != nil {
			return false
		}
		return cv < newFv || cv > newFv2
	case Gt:
		return cv > newFv
	case Gte:
		return cv >= newFv
	case Lt:
		return cv < newFv
	case Lte:
		return cv <= newFv
	}

	return false
}

func (filterValue *FilterValue) caseAny(compareVal interface{}, fv string, dataType DataType) bool {
	property := GetDataTypeProperty(dataType)
	switch property.BaseDataType {
	case String:
		return filterValue.caseString(compareVal, fv)
	case Number:
		return filterValue.caseNumber(compareVal, fv, "")
	case Any:
		return filterValue.caseAnyDataType(compareVal, fv, dataType)
	}

	return false
}

func (filterValue *FilterValue) caseAnyDataType(compareVal interface{}, fv string, dataType DataType) bool {
	// todo for any dataType
	switch dataType {
	case Radio:
		return helper.StrValue(compareVal) == fv
	case Select:
		// compareVal 可能是数字或字符串
		//compareValStr := helper.StrValue(compareVal)
		return helper.StrValue(compareVal) == fv
	}

	return false
}

func (filterValue *FilterValue) caseDate(compareVal interface{}, begin, end time.Time) bool {
	if compareVal == nil {
		return filterValue.FilterType == IsNull
	}
	var cTime time.Time
	switch v := compareVal.(type) {
	case string:
		if v == "" {
			break
		}
		dateTime, err := helper.ParseExcelDateTime(v, "YYYY-MM-DD")
		if err != nil {
			return false
		}
		cTime = dateTime
	case time.Time:
		cTime = v
	}

	switch filterValue.FilterType {
	case DateEnum:
		return (cTime.Equal(begin) || cTime.After(begin)) && cTime.Before(end)
	case NotDateEnum:
		return cTime.Before(begin) || cTime.Equal(end) || cTime.After(end)
	case Lt:
		return cTime.Before(begin)
	case Gt:
		return cTime.Equal(end) || cTime.After(end)
	case Between:
		return (cTime.Equal(begin) || cTime.After(begin)) && (cTime.Equal(end) || cTime.Before(end))
	case NotBetween:
		return cTime.Before(begin) || cTime.After(end)
	case IsNull:
		return cTime.IsZero()
	case HasValue:
		return !cTime.IsZero()
	}
	return false
}

func _parseString(val interface{}, fv string, parseFloat bool) (interface{}, interface{}, error) {
	if fv == "" {
		return nil, nil, errors.New("filter value is empty")
	}
	split := strings.Split(fv, ".")
	of := reflect.TypeOf(val)
	switch of.Kind() {
	case reflect.Int:
		if len(split) == 2 || parseFloat {
			res, err := strconv.ParseFloat(fv, 64)
			if err != nil {
				return nil, nil, err
			}
			return float64(val.(int)), res, nil
		}
		res, err := strconv.Atoi(fv)
		if err != nil {
			return nil, nil, err
		}
		return val, res, nil
	case reflect.Int8:
		if len(split) == 2 || parseFloat {
			res, err := strconv.ParseFloat(fv, 64)
			if err != nil {
				return nil, nil, err
			}
			return float64(val.(int8)), res, nil
		}
		res, err := strconv.ParseInt(fv, 10, 8)
		if err != nil {
			return nil, nil, err
		}
		return int64(val.(int8)), res, nil
	case reflect.Int16:
		if len(split) == 2 || parseFloat {
			res, err := strconv.ParseFloat(fv, 64)
			if err != nil {
				return nil, nil, err
			}
			return float64(val.(int16)), res, nil
		}
		res, err := strconv.ParseInt(fv, 10, 16)
		if err != nil {
			return nil, nil, err
		}
		return int64(val.(int16)), res, nil
	case reflect.Int32:
		if len(split) == 2 || parseFloat {
			res, err := strconv.ParseFloat(fv, 64)
			if err != nil {
				return nil, nil, err
			}
			return float64(val.(int32)), res, nil
		}
		res, err := strconv.ParseInt(fv, 10, 32)
		if err != nil {
			return nil, nil, err
		}
		return int64(val.(int32)), res, nil
	case reflect.Int64:
		if len(split) == 2 || parseFloat {
			res, err := strconv.ParseFloat(fv, 64)
			if err != nil {
				return nil, nil, err
			}
			return float64(val.(int64)), res, nil
		}
		res, err := strconv.ParseInt(fv, 10, 64)
		if err != nil {
			return nil, nil, err
		}
		return val, res, nil
	case reflect.Float32:
		res, err := strconv.ParseFloat(fv, 32)
		if err != nil {
			return nil, nil, err
		}
		return float64(val.(float32)), res, nil
	case reflect.Float64:
		res, err := strconv.ParseFloat(fv, 64)
		if err != nil {
			return nil, nil, err
		}
		return val, res, nil
	}
	return nil, nil, errors.New("type error")
}

func parseString(val interface{}, fv string) (interface{}, interface{}, error) {
	return _parseString(val, fv, false)
}

func parseStringFloat(val interface{}, fv string) (float64, float64, error) {
	i, i2, err := _parseString(val, fv, true)
	if err != nil {
		return 0, 0, err
	}
	return i.(float64), i2.(float64), nil
}

const (
	DefaultFilterType FilterType = iota // (0,"默认"),
	Like                                //(1,"包含"),
	Eq                                  //(2,"是(等于)"),
	Start                               //(3,"开头为"),
	End                                 //4,"结尾为"),
	NotContain                          //(5,"不包含"),
	Ne                                  //(6,"不等于"),
	IsNull                              //(7,"为空"),
	HasValue                            //(8,"不为空"),
	RelationEq                          //(9,"不为空"),
	Between                             //(10,"在范围内"),
	NotBetween                          //(11,"不在范围内"),
	Gt                                  //(12,">"),
	Gte                                 //(13,">="),
	Lt                                  //(14,"<"),
	Lte                                 //(15,"<="),
	DateEnum                            //(16,"日期是"),
	NotDateEnum                         //(17,"日期不是"),
	Myself                              //(18,"我拥有的"),
	Unread                              //(19,"未读"),
	Sub                                 //(20,"下属"),
	RcEq                                //(21,"关联控件是"),
	RcNe                                //(22,"关联控件不是"),
	ArrEq                               //(23,"数组等于"),
	ArrNe                               //(24,"数组不等于");
)

type BaseDataType int

const (
	Any BaseDataType = iota
	Boolean
	Number
	String
	Date
	Array
)

func (f FilterType) GetFilterExpression(filter SortInput) bson.M {
	filterValue := GetFilterValue(f)
	if filterValue == nil {
		return nil
	}

	result := bson.M{}
	if filter.DataType == DATE {
		begin, end := filter.GetDateRange()

		switch f {
		case DateEnum: // 日期是
			result[filter.Field] = bson.M{"$gte": begin, "$lt": end}
		case NotDateEnum: // 日期不是
			result = bson.M{
				"$or": bson.A{
					bson.M{
						filter.Field: bson.M{"$lt": begin},
					},
					bson.M{
						filter.Field: bson.M{"$gte": end},
					},
				},
			}
		case Lt: // 早于
			result[filter.Field] = bson.M{"$lt": begin}
		case Gt: // 晚于
			result[filter.Field] = bson.M{"$gte": end}
		case Between: // 在范围内
			result[filter.Field] = bson.M{"$gte": begin, "$lte": end}
		case NotBetween: // 不在范围内
			result = bson.M{
				"$or": bson.A{
					bson.M{
						filter.Field: bson.M{"$lt": begin},
					},
					bson.M{
						filter.Field: bson.M{"$gt": end},
					},
				},
			}
		case IsNull: // 为空
			result[filter.Field] = bson.M{"$type": 10}
		case HasValue: // 不为空
			result[filter.Field] = bson.M{"$type": 9}
		}

	} else if filter.Values != nil && len(filter.Values) > 0 {
		// 多个值，或 的关系
		arr := bson.A{}
		for i := 0; i < len(filter.Values); i++ {
			value := filter.Values[i]
			ex := filterValue.GetFilterExpression(value, filter.DataType)

			field := filter.DataType.FilterKey(filter.Field)

			if exArr, ok := ex.(bson.A); ok {
				fArr := bson.A{}
				for _, exA := range exArr {
					fArr = append(fArr, bson.M{
						field: exA,
					})
				}

				filter1 := bson.M{
					"$or": fArr,
				}
				arr = append(arr, filter1)
			} else {
				filter1 := bson.M{
					field: ex,
				}
				arr = append(arr, filter1)
			}

		}

		result = bson.M{"$or": arr}
	}

	return result
}

func (f FilterType) ResolveData(filter SortInput, val interface{}) bool {
	filterValue := GetFilterValue(f)
	if filterValue == nil {
		return false
	}

	if filter.DataType == DATE {
		begin, end := filter.GetDateRange()
		return filterValue.caseDate(val, begin, end)
	}

	if (filter.FilterType == Between || filter.FilterType == NotBetween) && filter.DataType != DATE {
		//if len(filter.Values) != 2 {
		//	return false
		//}
		return filterValue.caseNumber(val, filter.MinValue, filter.MaxValue)
	}

	flag := true
	for _, value := range filter.Values {
		data := filterValue.FilterData(val, value, filter.DataType)
		if !data {
			flag = false
			break
		}
	}

	return flag
}

// 获取正则表达式
func getFilterTypeValue(s, value string) interface{} {

	if ok := strings.HasPrefix(s, "/"); ok {
		s = strings.TrimPrefix(s, "/")
		s = strings.ReplaceAll(s, "{Value}", value)
		return bson.M{
			"$regex": s,
		}
	}

	return strings.ReplaceAll(s, "{Value}", value)
}

var FilterTypeMap = map[FilterType]FilterValue{
	DefaultFilterType: NewFilterValue("", "{Value}", Any, Any),
	Like:              NewFilterValue("$regex", ".*{Value}.*", String, String),
	Eq:                NewFilterValue("$eq", "{Value}", Any, Any),
	Start:             NewFilterValue("$regex", "^{Value}", String, String),
	End:               NewFilterValue("$regex", "{Value}$", String, String),
	NotContain:        NewFilterValue("$regex", "^((?!{Value}).)*$", String, String),
	Ne:                NewFilterValue("$ne", "{Value}", String, Any),
	IsNull:            NewFilterValue("$exists", false, Boolean, Any),
	HasValue:          NewFilterValue("$exists", true, Boolean, Any),
	RelationEq:        NewFilterValue("", "", Any, Any),                  // 不为空？
	Between:           NewFilterValue("", "", Any, Number),               //"在范围内",
	NotBetween:        NewFilterValue("", "", Any, Number),               // "不在范围内",
	Gt:                NewFilterValue("$gt", "{Value}", Number, Number),  // ">",
	Gte:               NewFilterValue("$gte", "{Value}", Number, Number), // ">=",
	Lt:                NewFilterValue("$lt", "{Value}", Number, Number),  // "<",
	Lte:               NewFilterValue("$lte", "{Value}", Number, Number), // "<=",
	DateEnum:          NewFilterValue("", "{Value}", String, Date),       // "日期是",
	NotDateEnum:       NewFilterValue("", "", Any, Date),                 // "日期不是",
	Myself:            NewFilterValue("", "", Any, Any),                  // "我拥有的",
	Unread:            NewFilterValue("", "", Any, Any),                  // "未读",
	Sub:               NewFilterValue("", "", Any, Any),                  // "下属",
	RcEq:              NewFilterValue("", "", Any, Any),                  // "关联控件是",
	RcNe:              NewFilterValue("", "", Any, Any),                  // "关联控件不是",
	ArrEq:             NewFilterValue("", "", Any, Any),                  // "数组等于",
	ArrNe:             NewFilterValue("", "", Any, Any),                  // "数组不等于",
}

func GetFilterValue(filterType FilterType) *FilterValue {
	filterValue, ok := FilterTypeMap[filterType]
	if !ok {
		return nil
	}
	filterValue.FilterType = filterType
	return &filterValue
}
